package com.example.freeengine.filetranslate;

import android.app.Application;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.util.Log;
import android.webkit.MimeTypeMap;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.OnLifecycleEvent;

import com.example.freeengine.R;
import com.example.freeengine.filetranslate.constants.FileTranslateConstant;
import com.example.freeengine.filetranslate.fragment.ClientInfo;
import com.example.freeengine.filetranslate.fragment.FileInfo;
import com.example.freeengine.filetranslate.fragment.RequestMessage;
import com.example.freeengine.filetranslate.fragment.ResponseMessage;
import com.example.freeengine.filetranslate.persistence.entity.FileTranslateDownloadFile;
import com.example.freeengine.filetranslate.persistence.entity.FileTranslateProgress;
import com.example.freeengine.filetranslate.persistence.repository.FileTranslateRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.koushikdutta.async.AsyncServer;
import com.koushikdutta.async.ByteBufferList;
import com.koushikdutta.async.DataEmitter;
import com.koushikdutta.async.callback.CompletedCallback;
import com.koushikdutta.async.callback.DataCallback;
import com.koushikdutta.async.http.WebSocket;
import com.koushikdutta.async.http.server.AsyncHttpServer;
import com.koushikdutta.async.http.server.AsyncHttpServerRequest;
import com.koushikdutta.async.http.server.AsyncHttpServerResponse;
import com.koushikdutta.async.http.server.HttpServerRequestCallback;

import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

public class FileTranslateViewModel extends AndroidViewModel implements LifecycleObserver {

    private static final String TAG = FileTranslateViewModel.class.getSimpleName();
    // uuid和客户端
    Map<String, ClientInfo> clientMap = new HashMap<>();
    // 已授权的客户端
    Set<WebSocket> authClient = new HashSet<>();

    // 有客户端连接
    MutableLiveData<String> clientConnect = new MutableLiveData<>("-");

    // 客户端发送的文件
    File tempUploadFile;
    FileOutputStream tempUploadFileOutputStream;

    // 文件传输进度相关
    FileTranslateRepository fileTranslateRepository;
    Map<WebSocket, Long> translateProgressMap = new HashMap<>();

    AsyncHttpServer httpServer = new AsyncHttpServer();
    AsyncHttpServer websocketServer = new AsyncHttpServer();
    boolean uploadServerStatus = false;

    AsyncHttpServer httpDownloadServer = new AsyncHttpServer();
    boolean downloadServerStatus = false;

    public FileTranslateViewModel(@NonNull @NotNull Application application) {
        super(application);
        fileTranslateRepository = new FileTranslateRepository(application);
    }
    public LiveData<String> getClientConnect(){
        return clientConnect;
    }
    public void setClientConnect(String data){
        this.clientConnect.postValue(data);
    }

    public Map<String, ClientInfo> getClientMap(){
        return clientMap;
    }

    public Set<WebSocket> getAuthClient(){
        return authClient;
    }

    public void initTempUploadFile(String path) throws IOException {
        tempUploadFile = new File(path);
        tempUploadFile.deleteOnExit();
        tempUploadFile.createNewFile();
//        File.createTempFile()
        try {
            tempUploadFileOutputStream = new FileOutputStream(tempUploadFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public void initTempUploadFile(String path, String fileName) throws IOException {

        File fileDir = new File(path);
        if (!fileDir.exists()){
            fileDir.mkdirs();
        }

        tempUploadFile = new File(fileDir, fileName);
        tempUploadFile.deleteOnExit();
        tempUploadFile.createNewFile();
//        File.createTempFile()
        try {
            tempUploadFileOutputStream = new FileOutputStream(tempUploadFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public void paddingCurrentFileData(byte[] data){
        if (tempUploadFile != null && tempUploadFileOutputStream != null){
            try {
                tempUploadFileOutputStream.write(data);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void paddingCurrentFileData(byte[] data, WebSocket webSocket){
        if (tempUploadFile != null && tempUploadFileOutputStream != null){
            try {
                tempUploadFileOutputStream.write(data);
                Long id = translateProgressMap.get(webSocket);
                if (id == null){
                    webSocket.close();
                    return;
                }
                FileTranslateProgress fileTranslateProgress = fileTranslateRepository.findById(id);
                int currentSize = fileTranslateProgress.getCurrentSize() + data.length;
                int progress = (int) (((currentSize * 1F) / fileTranslateProgress.getSize()) * 100);

                fileTranslateProgress.setCurrentSize(currentSize);
                fileTranslateProgress.setProgress(progress);

                if (currentSize == fileTranslateProgress.getSize()){
                    fileTranslateProgress.setStatus(1);
                }

                fileTranslateRepository.update(fileTranslateProgress);

            } catch (IOException | ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void closeTempUploadFile(){
        if (tempUploadFileOutputStream != null){
            try {
                tempUploadFileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public FileTranslateRepository getFileTranslateRepository(){
        return fileTranslateRepository;
    }

    public Map<WebSocket, Long> getTranslateProgressMap(){
        return translateProgressMap;
    }

    public String getFileMimeType(String path){
        String surffix = path.substring(path.lastIndexOf(".") + 1);
        Log.i("surffix", surffix);
        if (surffix.isEmpty()){
            return "file/*";
        }
        String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(surffix.toLowerCase());

        if (mimeType == null || mimeType.isEmpty()){
            return "file/*";
        }

        return mimeType;
    }

    public int getNetStatus(){
        int status = 0;
        ConnectivityManager connMgr = (ConnectivityManager) getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
        for (Network network : connMgr.getAllNetworks()) {
            NetworkInfo networkInfo = connMgr.getNetworkInfo(network);
            if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                if (networkInfo.isConnected()){
                    status |= 1;
                }
            }
            if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
                if (networkInfo.isConnected()){
                    status |= 2;
                }
            }
        }
        return status;
    }
    public String getIp(){
        WifiManager wifiManager = (WifiManager) getApplication().getSystemService(Context.WIFI_SERVICE);
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        return intToIp(wifiInfo.getIpAddress());
    }
    public static String intToIp(int ipInt) {
        StringBuilder sb = new StringBuilder();
        sb.append(ipInt & 0xFF).append(".");
        sb.append((ipInt >> 8) & 0xFF).append(".");
        sb.append((ipInt >> 16) & 0xFF).append(".");
        sb.append((ipInt >> 24) & 0xFF);
        return sb.toString();
    }

    public void startUploadHttpServer(){
        Log.i(TAG, "start upload http Server at: " + FileTranslateConstant.FILETRANSLATE_RECEIVE_HTTP_PORT);
        httpServer.get(FileTranslateConstant.FILETRANSLATE_RECEIVE_HOST_PATH, new HttpServerRequestCallback() {
            @Override
            public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {
                try(InputStream inputStream = getApplication().getResources().openRawResource(R.raw.filetranslate_home)){
                    response.sendStream(inputStream, inputStream.available());
                }catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        httpServer.listen(FileTranslateConstant.FILETRANSLATE_RECEIVE_HTTP_PORT);
    }

    public void startUploadWebsocketServer(){

        websocketServer.listen(AsyncServer.getDefault(), FileTranslateConstant.FILETRANSLATE_RECEIVE_HTTP_WEBSOCKET_PORT);
        Log.i(TAG, "start upload websocket Server at: " + FileTranslateConstant.FILETRANSLATE_RECEIVE_HTTP_WEBSOCKET_PORT);
        websocketServer.websocket(FileTranslateConstant.FILETRANSLATE_RECEIVE_HOST_WEBSOCKET_PATH, new AsyncHttpServer.WebSocketRequestCallback() {
            @Override
            public void onConnected(final WebSocket webSocket, AsyncHttpServerRequest request) {
                Log.i(TAG, "client connect: " );

                String uuid = UUID.randomUUID().toString();
                setClientConnect(uuid);
                getClientMap().put(uuid, new ClientInfo(webSocket, uuid, 0));

                ResponseMessage responseMessage = new ResponseMessage.Builder().setType(1).setContent(uuid).build();
                ObjectMapper objectMapper = new ObjectMapper();
                try {
                    webSocket.send(objectMapper.writeValueAsString(responseMessage));
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }

                webSocket.setClosedCallback(new CompletedCallback() {
                    @Override
                    public void onCompleted(Exception ex) {
                        try {
                            if (ex != null){
                                Log.e("WebSocket", "An error occurred", ex);
                            }
                        } finally {
                            Log.i(TAG, "websocket closed: " + uuid);
                            getClientMap().remove(uuid);
                            getAuthClient().remove(webSocket);
                            getTranslateProgressMap().remove(webSocket);
                            webSocket.close();
                        }
                    }
                });

                webSocket.setStringCallback(new WebSocket.StringCallback() {
                    @Override
                    public void onStringAvailable(String s) {

                        // 检查auth
                        if (!getAuthClient().contains(webSocket)){
                            Log.i(TAG, "client no auth");
                            getAuthClient().remove(webSocket);
                            webSocket.close();
                            return;
                        }

                        Log.i("WebSocket", "get string: " + s);
                        ObjectMapper objectMapper = new ObjectMapper();
                        try {
                            RequestMessage requestMessage = objectMapper.readValue(s, RequestMessage.class);
                            switch (requestMessage.getType()){
                                case 1: {// 文件信息
                                    ResponseMessage responseMessage = new ResponseMessage.Builder().setType(3).setContent("").build();
                                    FileInfo fileInfo = objectMapper.readValue(requestMessage.getContent(), FileInfo.class);
                                    String fileName = System.currentTimeMillis() + fileInfo.getName();
                                    initTempUploadFile(getApplication().getFilesDir() + FileTranslateConstant.FILETRANSLATE_RECEIVE_FILE_PATH,
                                            fileName);

                                    // 同名文件覆盖
                                    FileTranslateProgress fileTranslateProgress = new FileTranslateProgress();
                                    fileTranslateProgress.setName(fileInfo.getName());
                                    fileTranslateProgress.setFilePath(getApplication().getFilesDir() + FileTranslateConstant.FILETRANSLATE_RECEIVE_FILE_PATH + fileName);
                                    fileTranslateProgress.setProgress(0);
                                    fileTranslateProgress.setStatus(0);
                                    fileTranslateProgress.setSize(fileInfo.getSize());
                                    fileTranslateProgress.setCurrentSize(0);
                                    fileTranslateProgress.setCreateTimestamp(System.currentTimeMillis());

                                    try {
                                        Long id = getFileTranslateRepository().insert(fileTranslateProgress);
                                        getTranslateProgressMap().put(webSocket, id);
                                    } catch (ExecutionException | InterruptedException e) {
                                        e.printStackTrace();
                                    }

                                    try {
                                        webSocket.send(objectMapper.writeValueAsString(responseMessage));
                                    } catch (JsonProcessingException e) {
                                        e.printStackTrace();
                                    }
                                }break;
                                case 2: {// 文件发送完毕
                                    Log.i(TAG, "file upload end");
                                    closeTempUploadFile();
                                }break;
                                default:
                            }


                        } catch (IOException e) {
                            e.printStackTrace();
                            Log.i(TAG, "数据格式错误");
                        }
                    }
                });

                webSocket.setDataCallback(new DataCallback() {
                    @Override
                    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {

                        // 检查auth
                        if (!getAuthClient().contains(webSocket)){
                            Log.i(TAG, "client no auth");
                            getAuthClient().remove(webSocket);
                            webSocket.close();
                            return;
                        }

                        byte[] allByteArray = bb.getAllByteArray();
                        paddingCurrentFileData(allByteArray, webSocket);
                        Log.i(TAG, "get data size: " + allByteArray.length);
                    }
                });

            }
        });
    }

    public void stopUploadHttpServer(){
        if (httpServer != null){
            httpServer.stop();
            setUploadServerStatus(false);
            Log.i(TAG, "stop UploadHttpServer");
        }
    }
    public void stopUploadWebsocketServer(){
        if (websocketServer != null){
            websocketServer.stop();
            setUploadServerStatus(false);
            Log.i(TAG, "stop UploadWebsocketServer");
        }
    }
    public void stopDownloadHttpServer(){
        if (httpDownloadServer != null){
            httpDownloadServer.stop();
            setDownloadServerStatus(false);
            Log.i(TAG, "stop DownloadHttpServer");
        }
    }

    public void stopAllServer(){



        stopUploadHttpServer();
        stopUploadWebsocketServer();
        stopDownloadHttpServer();
    }

    public void startAllServer(){
        startUploadHttpServer();
        startUploadWebsocketServer();
        setUploadServerStatus(true);
        startDownloadHttpServer();
        setDownloadServerStatus(true);
    }

    public void setUploadServerStatus(boolean status){
        uploadServerStatus = status;
    }
    public boolean getUploadServerStatus(){
        return uploadServerStatus;
    }
    public void setDownloadServerStatus(boolean status){
        downloadServerStatus = status;
    }
    public boolean getDownloadServerStatus(){
        return downloadServerStatus;
    }

    public void startDownloadHttpServer(){
        Log.i(TAG, "start download http Server at: " + FileTranslateConstant.FILETRANSLATE_DOWNLOAD_HTTP_PORT);
        // 下载主页
        httpDownloadServer.get(FileTranslateConstant.FILETRANSLATE_DOWNLOAD_HOST_PATH, new HttpServerRequestCallback() {
            @Override
            public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {
                try(InputStream inputStream = getApplication().getResources().openRawResource(R.raw.filetranslate_send)){
                    response.sendStream(inputStream, inputStream.available());
                }catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        // 查询可下载文件列表
        httpDownloadServer.get("/download/fileListQuery", new HttpServerRequestCallback() {
            @Override
            public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {

                List<FileTranslateDownloadFile> downloadFileList = new ArrayList<>();
                try {
                    downloadFileList = getFileTranslateRepository().findAllDownloadFilesBlock();
                } catch (ExecutionException | InterruptedException | TimeoutException e) {
                    e.printStackTrace();
                }

                Log.i(TAG, "file list size: " + downloadFileList.size());
                ObjectMapper objectMapper = new ObjectMapper();
                try {
                    String s = objectMapper.writeValueAsString(downloadFileList);
                    response.send(s);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }

            }
        });

        // 查询可下载文件列表
        httpDownloadServer.get("/download/fileQuery", new HttpServerRequestCallback() {
            @Override
            public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {

                String filePath = request.getQuery().getString("filePath");
                Log.i(TAG, "filePath: " + filePath);
                File file = new File(filePath);

                if (!file.exists()){
                    response.send("no");
                }else {
                    response.send("yes");
                }
            }
        });

        // 传输文件
        httpDownloadServer.get("/download/file", new HttpServerRequestCallback() {
            @Override
            public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {
                String filePath = request.getQuery().getString("filePath");
                File file = new File(filePath);

                if (file.exists()){
                    response.sendFile(file);
                }else {
                    response.send("文件不存在了");
                }

            }
        });
        httpDownloadServer.listen(FileTranslateConstant.FILETRANSLATE_DOWNLOAD_HTTP_PORT);
    }

//    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
//    public void onStop(){
//        stopAllServer();
//    }
}